package com.vincce.sport.core.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Redis操作类，封装了 缓存、队列、计数 等相关方法
 *
 * User: Henry Young
 * Date: 11/25/11
 * Time: 4:22 PM
 */
public class RedisUtil {

	private static JedisPool jedisPool;

//	public static final String CACHE_KEYS_INDEX = "_keys";
	public static final String CACHE_KEYS_SET = "_keys_set";
	public static final String CACHE_ALWAYS_KEYS_INDEX = "_always_keys";
	public static final int DB_NUMBER_ALWAYS = 1;
	public static final int DB_USER_SESSION = 2;
	public static final int DB_CMS_ARTICLE = 4;
	public static final int DB_APP_ARTICLE = 5;
	public static final int DB_AUTH_LOGIN = 6;
    //手机端安全中心
    public static final int DB_SAFE_CENTER = 7;
    //首页数据
  	public static final int DB_SPORT_DATEVIEW = 8;
  	//邀请码
  	public static final int DB_SPORT_CODE = 9;
	private static String redisServers;
	
	private static String redisKey;

	

	/**
	 * 初始化 系统会维护不要调用
	 */
	public static synchronized void init() {
		JedisPoolConfig poolConfig = new JedisPoolConfig();
		// if (!CheckUtil.isEmpty("192.168.0.6:6379")) {
		if (!CheckUtil.isEmpty(redisServers)) {
			String[] ss = redisServers.split(":");
			jedisPool = new JedisPool(poolConfig, ss[0], Integer.valueOf(ss[1]));
		}
	}

	
	public void setRedisServers(String redisServers) {
		this.redisServers = redisServers;
		init();
	}

	public void setRedisKey(String redisKey) {
		this.redisKey = redisKey;
	}


	/**
	 * 停止Redis连接池方法（需在容器销毁时调用）
	 */
	public static void stop() {
		jedisPool.destroy();
	}

	/**
	 * 获取Redis连接方法
	 * @param dbNumber 数据库编号
	 * @return Jedis连接对象
	 */
	public static Jedis getResource(Integer dbNumber) {
		Jedis jedis = jedisPool.getResource();
		jedis.select(dbNumber);
		return jedis;
	}

	/**
	 * 归还Redis连接方法
	 * @param jedis Jedis连接对象
	 */
	public static void returnResource(Jedis jedis) {
		jedisPool.returnResource(jedis);
	}

	/**
	 * 缓存 添加/更新方法（需手动过期）
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @param value 值
	 * @param expiry 过期时间，单位秒（-1 = 永久有效）
	 */
	public static void cacheAddUpdateManually(Integer dbNumber, String key, Object value, Integer expiry) {
		if (value == null) {
			return;
		}
		String v = getString(value);
		Jedis jedis = getResource(dbNumber);
		Transaction transaction = jedis.multi();
		transaction.set(key, v);
		if (expiry.equals(-1)) {
			transaction.sadd(CACHE_ALWAYS_KEYS_INDEX, key);
		} else {
			transaction.zadd(CACHE_KEYS_SET, System.currentTimeMillis() / 1000 + expiry, key);
		}
		transaction.exec();
		returnResource(jedis);
	}

	/**
	 * 缓存 添加/更新方法
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @param value 值
	 * @param expiry 过期时间，单位秒（-1 = 永久有效）
	 */
	public static void cacheAddUpdate(Integer dbNumber, String key, Object value, Integer expiry) {
		if (value == null) {
			return;
		}
		String v = getString(value);
		Jedis jedis = getResource(dbNumber);
		if (expiry.equals(-1)) {
			Transaction transaction = jedis.multi();
			transaction.set(redisKey+key, v);
			transaction.sadd(CACHE_ALWAYS_KEYS_INDEX, redisKey+key);
			transaction.exec();
		} else {
			jedis.setex(redisKey+key, expiry, v);
//			transaction.sadd(CACHE_KEYS_INDEX, key);
//			transaction.zadd(CACHE_KEYS_SET, System.currentTimeMillis() / 1000 + expiry, key);
		}
		returnResource(jedis);
	}

	/**
	 * 永久缓存 添加/更新方法
	 * @param key 键
	 * @param value 值
	 */
	public static void foreverCacheAddUpdate(String key, Object value) {
		cacheAddUpdate(DB_NUMBER_ALWAYS, key, value, -1);
	}

//	/**
//	 * 缓存 添加/更新方法
//	 * @param dbNumber 数据库编号
//	 * @param key 键
//	 * @param value 值
//	 * @param expiry 过期时间，单位秒（-1 = 永久有效）
//	 */
//	public static void cacheAddUpdate(Integer dbNumber, String key, String value, Integer expiry) {
//		Jedis jedis = getResource(dbNumber);
//		Transaction transaction = jedis.multi();
//		if (expiry.equals(-1)) {
//			transaction.set(key, value);
//			transaction.sadd(CACHE_ALWAYS_KEYS_INDEX, key);
//		} else {
//			transaction.setex(key, expiry, value);
//			transaction.sadd(CACHE_KEYS_INDEX, key);
//		}
//		transaction.exec();
//		returnResource(jedis);
//	}


	/**
	 * 永久缓存获取方法（使用json反序列化）
	 * @param key 键
	 * @param type 对象类型引用
	 * @return 内容
	 */
	public static <T> T foreverCacheGet(String key, TypeReference<T> type) {
		return cacheGet(DB_NUMBER_ALWAYS, key, type);
	}

	/**
	 * 永久缓存获取方法（使用json反序列化）
	 * @param key 键
	 * @param type 对象类型引用
	 * @return 内容
	 */
	public static <T> T foreverCacheGet(String key, Class<T> type) {
		return cacheGet(DB_NUMBER_ALWAYS, key, type);
	}

	/**
	 * 缓存 获取方法（重载方法，使用json反序列化）
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @param type 对象类型引用
	 * @return 内容
	 */
	public static <T> T cacheGet(Integer dbNumber, String key, TypeReference<T> type) {
		String s = cacheGet(dbNumber, key);
		return s == null ? null : JSON.parseObject(s, type);
	}

	/**
	 * 缓存 获取方法（重载方法，使用json反序列化）
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @param type 对象类型
	 * @return 内容
	 */
	public static <T> T cacheGet(Integer dbNumber, String key, Class<T> type) {
		String s = cacheGet(dbNumber, key);
		return s == null ? null : JSON.parseObject(s, type);
	}

	/**
	 * 缓存 获取方法
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @return 内容
	 */
	public static String cacheGet(Integer dbNumber, String key) {
		return get(dbNumber, key);
	}

	/**
	 * 缓存 更改过期时间方法（需手动过期）
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @param expiry 过期时间，单位秒
	 */
	public static void cacheExpiryManually(Integer dbNumber, String key, Integer expiry) {
		Jedis jedis = getResource(dbNumber);
		jedis.zadd(CACHE_KEYS_SET, System.currentTimeMillis() / 1000 + expiry, key);
		returnResource(jedis);
	}

	/**
	 * 缓存 更改过期时间方法
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @param expiry 过期时间，单位秒
	 */
	public static void cacheExpiry(Integer dbNumber, String key, Integer expiry) {
		Jedis jedis = getResource(dbNumber);
		jedis.expire(key, expiry);
		returnResource(jedis);
	}

	/**
	 * 缓存 移除方法
	 * @param dbNumber 数据库编号
	 * @param key 键
	 */
	public static void cacheRemove(Integer dbNumber, String key) {
		remove(dbNumber, key);
	}

	/**
	 * 缓存 获取所有过期key方法（调用后需要手动remove过期数据）
	 * @param dbNumber 数据库编号
	 * @return key集合
	 */
	public static Set<String> getExpiredCaches(Integer dbNumber) {
		Jedis jedis = getResource(dbNumber);
		Set<String> result = jedis.zrangeByScore(CACHE_KEYS_SET, 0, System.currentTimeMillis() / 1000);
		returnResource(jedis);
		return result;
	}

	/**
	 * 缓存 判断是否存在方法
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @return 是否存在
	 */
	public static Boolean cacheExists(Integer dbNumber, String key) {
		return exists(dbNumber, key);
	}

	/**
	 * 队列 存入方法（重载方法，使用JSON序列化）
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @param value 值
	 */
	public static void sqsPush(Integer dbNumber, String key, Object value) {
		if (value == null) {
			return;
		}
		String v = getString(value);
		Jedis jedis = getResource(dbNumber);
		jedis.lpush(key, v);
		returnResource(jedis);
	}

	/**
	 * 队列 存入方法
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @param value 值
	 */
	public static void sqsPush(Integer dbNumber, String key, String value) {
		Jedis jedis = getResource(dbNumber);
		jedis.lpush(key, value);
		returnResource(jedis);
	}

	/**
	 * 队列 取出方法（重载方法，使用JSON反序列化）
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @param type 对象类型
	 * @return 值
	 */
	public static <T> T sqsPop(Integer dbNumber, String key, Class<T> type) {
		String s = sqsPop(dbNumber, key);
		return s == null ? null : JSON.parseObject(s, type);
	}

	/**
	 * 队列 取出方法（重载方法，使用JSON反序列化）
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @param type 对象类型引用
	 * @return 值
	 */
	public static <T> T sqsPop(Integer dbNumber, String key, TypeReference<T> type) {
		String s = sqsPop(dbNumber, key);
		return s == null ? null : JSON.parseObject(s, type);
	}

	/**
	 * 队列 取出方法
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @return 值
	 */
	public static String sqsPop(Integer dbNumber, String key) {
		Jedis jedis = getResource(dbNumber);
		String result = jedis.rpop(key);
		returnResource(jedis);
		return result;
	}
	
	/**
	 * 队列 取出方法(取出所有数据)
	 * @param <T> 泛型
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @param type 队列类型
	 * @return 值
	 * @author zbl
	 */
	public static <T> List<T> sqsPopAll(Integer dbNumber, String key, Class<T> type){
		return sqsPop(dbNumber, key, 0, -1, type);
	}
	
	/**
	 * 队列 取出方法（用于实现分页）
	 * @param <T> 泛型
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @param start 开始索引号
	 * @param end 结束索引号
	 * @param type 队列类型
	 * @return 值
	 * @author zbl
	 */
	public static <T> List<T> sqsPop(Integer dbNumber, String key, long start ,long end, Class<T> type){
		Jedis jedis = getResource(dbNumber);
		List<String> result = jedis.lrange(key, start, end);
		returnResource(jedis);
		if(null==result){
			return null;
		}
		List<T> list=new ArrayList<T>();
		for(String s : result){
			list.add(JSON.parseObject(s, type));
		}
		return list;
	}

	/**
	 * 队列 获取总数方法（若只是判断队列里是否有值，请直接调用sqsPop方法判断）
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @return 总数
	 */
	public static Long sqsCount(Integer dbNumber, String key) {
		Jedis jedis = getResource(dbNumber);
		Long result = jedis.llen(key);
		returnResource(jedis);
		return result;
	}

	/**
	 * 计数 自增1方法
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @return 自增后数字
	 */
	public static Long incrAdd(Integer dbNumber, String key) {
		Jedis jedis = getResource(dbNumber);
		Long result = jedis.incr(key);
		returnResource(jedis);
		return result;
	}

	/**
	 * 计数 获取方法
	 * @param dbNumber 数据库编号
	 * @param key 键
	 * @return 当前数字
	 */
	public static Long incrGet(Integer dbNumber, String key) {
		String result = get(dbNumber, key);
		return result == null ? null : Long.valueOf(result);
	}

	private static Boolean exists(Integer dbNumber, String key) {
		Jedis jedis = getResource(dbNumber);
		Boolean result = jedis.exists(redisKey+key);
		returnResource(jedis);
		return result;
	}

	private static void remove(Integer dbNumber, String key) {
		Jedis jedis = getResource(dbNumber);
		jedis.del(redisKey+key);
		returnResource(jedis);
	}

	private static String get(Integer dbNumber, String key) {
		Jedis jedis = getResource(dbNumber);
		String result = jedis.get(redisKey+key);
		returnResource(jedis);
		return result;
	}

	private static String getString(Object value) {
		if (value instanceof String) {
			return (String) value;
		} else {
			return JSON.toJSON(value).toString();
		}
	}
	
	

}
